package top.chenyaohua.asm;


import org.objectweb.asm.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class CustomClassVisit extends ClassVisitor implements Opcodes {

    private BeanMetadata beanMetadata;

    public CustomClassVisit ( ClassVisitor classVisitor,BeanMetadata beanMetadata) {
        super(Opcodes.ASM5, classVisitor);
        this.beanMetadata = beanMetadata;
    }

    @Override
    public AnnotationVisitor visitAnnotation (String desc, boolean visible) {
        //解析类注解
        List<AnnotationMetadata> annos = Optional.ofNullable(beanMetadata.getAnnotations()).orElseGet(ArrayList::new);
        AnnotationMetadata annotationMetadata = new AnnotationMetadata(desc);
        annos.add(annotationMetadata);
        this.beanMetadata.setAnnotations(annos);

        return new CustomAnnotationVisitor( super.visitAnnotation(desc, visible) ,this,null,null);
    }


    //解析属性
    @Override
    public FieldVisitor visitField (int access, String name, String desc, String signature, Object value) {

        List<FieldMetadata> list =  Optional.ofNullable(beanMetadata.getFieldMetadata()).orElseGet(ArrayList::new);
        FieldMetadata fieldMetadata = new FieldMetadata(name,desc);
        list.add(fieldMetadata);
        this.beanMetadata.setFieldMetadata(list);

        FieldVisitor fieldVisitor = super.visitField(access, name, desc, signature, value);
        CustomFieldVisitor customFieldVisitor = new CustomFieldVisitor(fieldVisitor,this);

        return customFieldVisitor;
    }

    @Override
    public MethodVisitor visitMethod (int access, String name, String desc, String signature, String[] exceptions) {
        //解析助记符获取方法返回值类型
        Type returnType = Type.getReturnType(desc);
        //解析助记符获取方法参数类型
        Type[] argumentTypes = Type.getArgumentTypes(desc);
        //解析 access 获取方法权限信息\是否静态\是否存在final关键字
//ACC_PUBLIC + ACC_FINAL + ACC_STATIC + ACC_SYNCHRONIZED
        MethodMetadata methodMetadata = new MethodMetadata(name,isKey(access).isAccStatic(),exceptions,returnType.getClassName(),argumentTypes.length);
        List<MethodMetadata> methods = Optional.ofNullable(this.beanMetadata.getMethodMetadata()).orElseGet(ArrayList::new);
        methods.add(methodMetadata);
        this.beanMetadata.setMethodMetadata(methods);
        //解析方法
        return new CustomMethodVisit( super.visitMethod(access, name, desc, signature, exceptions) ,this);
    }

    public BeanMetadata getBeanMetadata () {
        return beanMetadata;
    }


    protected AccessInfo isKey(int access){
        final int a = ACC_PUBLIC + ACC_STATIC;
        final int b = ACC_PUBLIC + ACC_STATIC + ACC_FINAL;
   /*     final int l = ACC_PUBLIC + ACC_STATIC + ACC_SYNCHRONIZED;
        final int l = ACC_PUBLIC + ACC_STATIC + ACC_ABSTRACT;
                final int l = ACC_PUBLIC + ACC_STATIC + ACC_ABSTRACT;

        */
        //3*2*2*2*2=48
        final int c = ACC_PUBLIC + ACC_STATIC + ACC_FINAL+ACC_ABSTRACT;
        final int d = ACC_PUBLIC + ACC_STATIC + ACC_FINAL+ACC_ABSTRACT+ACC_SYNCHRONIZED;




        final int e = ACC_PRIVATE + ACC_STATIC;
        final int f = ACC_PRIVATE + ACC_STATIC + ACC_FINAL;
        final int g = ACC_PRIVATE + ACC_STATIC + ACC_FINAL+ACC_ABSTRACT;
        final int h = ACC_PRIVATE + ACC_STATIC + ACC_FINAL+ACC_ABSTRACT+ACC_SYNCHRONIZED;

        final int j = ACC_PROTECTED + ACC_STATIC;
        final int k = ACC_PROTECTED + ACC_STATIC + ACC_FINAL;
        final int m = ACC_PROTECTED + ACC_STATIC + ACC_FINAL+ACC_ABSTRACT;
        final int n = ACC_PROTECTED + ACC_STATIC + ACC_FINAL+ACC_ABSTRACT+ACC_SYNCHRONIZED;

        AccessInfo accessInfo = new AccessInfo();

        switch (access){
            case a:
                accessInfo.setAccEncapsulation(ACC_PUBLIC);
                accessInfo.setAccStatic(true);
                break;
            case b:
                accessInfo.setAccEncapsulation(ACC_PUBLIC);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                break;
            case c:
                accessInfo.setAccEncapsulation(ACC_PUBLIC);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                accessInfo.setAccAbstract(true);
                break;
            case d:
                accessInfo.setAccEncapsulation(ACC_PUBLIC);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                accessInfo.setAccAbstract(true);
                accessInfo.setAccSynchronized(true);
                break;
            case e:
                accessInfo.setAccEncapsulation(ACC_PRIVATE);
                accessInfo.setAccStatic(true);
                break;
            case f:
                accessInfo.setAccEncapsulation(ACC_PRIVATE);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                break;
            case g:
                accessInfo.setAccEncapsulation(ACC_PRIVATE);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                accessInfo.setAccAbstract(true);
                break;
            case h:
                accessInfo.setAccEncapsulation(ACC_PRIVATE);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                accessInfo.setAccAbstract(true);
                accessInfo.setAccSynchronized(true);
                break;
            case j:
                accessInfo.setAccEncapsulation(ACC_PROTECTED);
                accessInfo.setAccStatic(true);
                break;
            case k:
                accessInfo.setAccEncapsulation(ACC_PROTECTED);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                break;
            case m:
                accessInfo.setAccEncapsulation(ACC_PROTECTED);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                accessInfo.setAccAbstract(true);
                break;
            case n:
                accessInfo.setAccEncapsulation(ACC_PROTECTED);
                accessInfo.setAccStatic(true);
                accessInfo.setAccFinal(true);
                accessInfo.setAccAbstract(true);
                accessInfo.setAccSynchronized(true);
                break;

        }
        return accessInfo;
    }
}
